import Vue, { DirectiveOptions } from 'vue'
import { on } from '@/utils/dom'

let counter: number = 0
const nodeList = []
const ctx = '@@com-clickOutsideContext@'

let startClick: MouseEvent

if (!Vue.prototype.$isServer) {
  on(document, 'mousedown', e => {
    startClick = e as MouseEvent
  })

  on(document, 'mouseup', e => {
    nodeList.forEach(node => node[ctx].documentHandler(e, startClick))
  })
}

const clickoutside: DirectiveOptions = {
  bind (el, binding, vnode) {
    const id = counter++
    nodeList.push(el)
    const handler = function (upEvent: MouseEvent, downEvent: MouseEvent): void {
      if (
        !vnode.context ||
        !upEvent ||
        !downEvent ||
        !upEvent.target ||
        !downEvent.target ||
        el.contains(upEvent.target as Node) ||
        el.contains(downEvent.target as Node) ||
        el === upEvent.target
      ) {
        return
      }
      if (
        binding.expression &&
        el[ctx].methodName &&
        vnode.context[el[ctx].methodName]
      ) {
        vnode.context[el[ctx].methodName]()
      } else {
        el[ctx].bindingFn && el[ctx].bindingFn()
      }
    }
    el[ctx] = {
      id,
      documentHandler: handler,
      methodName: binding.expression,
      bindingFn: binding.value
    }
  },
  update (el, binding) {
    el[ctx].methodName = binding.expression
    el[ctx].bindingFn = binding.value
  },
  unbind (el) {
    let len = nodeList.length

    for (let i = 0; i < len; i++) {
      if (nodeList[i][ctx].id === el[ctx].id) {
        nodeList.splice(i, 1)
        break
      }
    }
  }
}

export default clickoutside
